//
//  ViewController.swift
//  Study Swift
//
//  Created by admin on 2019/8/5.
//  Copyright © 2019 猫爷爷的酱醋茶. All rights reserved.
//

/*
 
 Swift
    var的可选项默认值为nil
    let的可选项没有默认值，必须要设置初始值
 
    OC中的所有对象都继承自NSObject
    Swift中的对象，可以没有任何父类 用Any表示
 
    在Swift中，函数本身可以当作参数传递
    在Swift中，默认所有的属性和方法都是全局共享的，不需要import，可以直接使用
 
    === 可以用来检查两个变量时候确实引用了同一个对象。
    == 有时候被称为结构相等， === 则被称为指针相等或者引用相等
 
 方法和自有函数：
 
    定义在类或者协议中的函数就是方法(method)，它们有一个隐式的self参数。
    有时候我们会把那些不是方法函数叫做自由函数。
 
 静态派发和动态派发：
 
    自由函数和那些在结构体上调用的方法是静态派发。
    对于这些函数的调用，在编译的时候就已经确定了。对于静态派发的调用，编译器可能能够内联（inline）
 这些函数，也就是说，完全不去做函数调用，而是将函数调用替换为函数中需要的执行的代码。
    优化器还能够帮助丢弃或者简化那些在编译时就能确定不会被实际执行的代码。
 
    
 
 
 
 1. a）对数据类型要求的很严格，不会自动做隐性转换，不同类型的数据不能直接计算，需要进行类型转换。
    b）条件判断，C语言中有一个非零既真的判断，但是Swift不存在，只有true/false。
 
 2. Swift中的整数默认是int，64位，相当于OC中的long类型
    Swift中的小数默认为double是双精度型，OC中使用 CGFloat比较多
 
 3. 定义变量/常量如果需要指定的类型
    let/var 变量名：数据类型
 
 4. 可选项Optional ？ - 作用于变量，要么有值，要么为nil
    let num1：Int？  //那么num1可以是一个整数，也可以是nil，默认值为nil。
 
    强行解包unwrapping。
 
 5. if let 可以设置数值，进入分支后，保证有值 //语句中不能使用&& || 条件 而是用where
 
    if let name = name
     {
        //如果条件满足，执行这里
     }
 
 6. guard let name = name else
     {
     //如果条件不满足，执行这里
        return
     }
 
    //代码运行至此，表示一定有值。
 
 7. 变量 ?? ""  操作符 可以判断变量时候为nil，如果为nil，使用后面的替换
 
 8. switch
    Swift中的switch 与OC的区别
    a）不需要break case之间也不会造成代码穿透
    b）不再局限于整数，可以是任意数据类型进行检测
    c）定义变量不需要用花括号{}确定作用域
    d）每一个case分支必须包含一条语句 没有语句就写break；
    e）也可以省略条件的值，用where做条件判断  case _ where 条件判断:
 
 9. 字符串
    Swift中是 String类型，本质是一个Struct结构体 - 量级更轻
    OC中是 NSString类型，本质是继承NSObject的一个对象
 
    Swift 字符串的拼接 \(变量名) 方式
 
    字符串的遍历
    for c in string.characters
     {
        print(c);
     }
 
    字符串的长度
    string.characters.count
 
 10. for循环
     Swift舍弃了C语言的for循环 而是使用 for _ in _
     a.可以是一段范围  for i in 2...9/  for i in 2..<9
     b.也可以是一个集合（数组、字典）  for _ in array
 
 11. 数组 - 大部分存放相同类型的数据
     与OC不同的是
                1.数组中可以存放不同类型的数据 类型是[Any] - 任意对象
                2.存放数字不需要包装成NSNumber
                3.存放结构体（CGPoint）也不需要包装成NSValue
                4.OC中追加数组用 add
                  Swift中追加数组用array.append
 
     数组中的可变和不可变
     - OC中 可变是NSMutableArray 不可变是NSArray
     - Swift中 可变是 var 不可变是 let
 
 12. 闭包 - 类是OC中的block
 
    block - 是C语言的
          - 是一组预先准备好的代码，在需要的时候执行
          - 可以当作参数传递
          - 如果出现self注意循环引用
 
    定义闭包
            a）闭包的所有代码都放在{}中，包括参数、返回值、执行代码
            b）in 是用来区分函数定义和执行代码
 
    尾随闭包： - 要求能看懂
            a）闭包的参数是函数的最后一个参数
            b）函数的）结束可以前置，倒数第二个参数的末尾
            c）最后一个逗号可以省略
 
    使用场景
            通常用在异步加载网络数据，完成回调
 

 */

import UIKit

class ViewController: UIViewController {
    
    /*
     懒加载 - 本质上是一个闭包
        第一次访问属性时，会执行后面的闭包代码，将闭包的‘结果’保存在 person 属性中
        下次访问的时候，不会再执行闭包
     
     lazy - 可以让懒加载尽可能的懒
         如果没有lazy 会在 initWitchCode 方法中被调用，
         当二进制的 stroyboard 被还原成视图控制器对象之后，就会被调用
     
     */
    lazy var person:Person =
    {
       return Person()
    }()
    
    //懒加载的简单写法
    lazy var person2:Person = Person()
    
    //定义完成回调属性
    var finishedCallBack: ((_ html:String)->())?

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
//        let p = Person()
        
//        let m = Student(dict: ["name":"猫爷爷的酱醋茶" as AnyObject ,"age":22 as AnyObject,"no":"37" as AnyObject])
//
//        print("\(String(describing: m.name)) -- \(String(describing: m.age)) -- \(String(describing: m.no))")
        
//        print(p);
//        //尾随闭包
//        loadData {
//            //调用函数前准备的代码，以参数的形式传递
//            print("回调执行代码")
//        }
       
//        deserialization()
        
        //解除循环引用
        demoWeakSwift()
        
    }
    
    deinit {
        print("控制器 88")
    }
    
    
    
    //MARK: 解除循环引用
    
    func demoWeakSwift()
    {
        //[weak self] 表示闭包中的self 都是 弱引用
        //与 __weak 类是，如果self被释放，什么也不做，更安全
        loadData2 { [weak self] (html) in
            print(html)
            print(self?.view as Any)
        }
    }
    
    func demoWeakOC()
    {
        //方式一：OC 的传统方法
        //weak 属性在运行过程中，可能被改变，执行对象一旦释放，变成nil 所以需要用var
        weak var weakSelf = self
        
        loadData2 { (html) in
            print(html)
            //闭包中，一定要用self. 用来区分执行语境下的归属
            //            print(self.view as Any)
            
            //weakSelf? 表示对象一旦被释放，不再访问其属性或者方法
            print(weakSelf?.view as Any)
        }
    }
    
    //MARK: 循环引用
    func demoCircularReference()
    {
        
        loadData2 { (html) in
            print(html)
            //闭包中，一定要用self. 用来区分执行语境下的归属
            print(self.view as Any)
        }
    }
    
    
    //闭包应用场景：异步操作完成后，通过闭包的参数传递网络请求结果
    func loadData2(finshed:@escaping (_ html:String)->())
    {
        //记录完成回调
        finishedCallBack = finshed
        
        DispatchQueue.global().async {
            print("模拟异步加载数据")
            
            //主线回调
            DispatchQueue.main.async {
                print("主线程回调")
                //如果回调不能在当前方法立即执行，可以通过属性记录，在需要的时候用执行
//                finshed("<html>")
                
                self.finishedCallBack?("haha")
            }
        }
    }
    
    //MARK:反序列化
    
    func deserialization()
    {
        let url = URL(string: "http://www.weather.com.cn/weather/101020100.shtml")
        
        URLSession.shared.dataTask(with: url!) { (data, _, _) in
            
            print(data as Any);
            
            //反序列化
           let result = try? JSONSerialization.jsonObject(with: data!, options: [JSONSerialization.ReadingOptions.mutableContainers,JSONSerialization.ReadingOptions.mutableLeaves])
            
            print(result as Any)
            
        }.resume()
    }
    
     //MARK: - 闭包
    
    //异步加载
    func loadData(finished:@escaping ()->())
    {
        
        DispatchQueue.global().async {
            print("耗时操作 \(Thread.current)")
            
            //通常加载数据...
            
            //异步完成之后，通过block回调
            DispatchQueue.main.async(execute: {
                print("完成 \(Thread.current)")
                
                //执行调用方准备好的代码 （）表示执行
                finished()
            })
        }
        
    }
    
    func demo()
    {
        
        //定义闭包
        let sumFunc = { (num1:Int ,num2:Int) -> Int in
            return num1 + num2
        }
        
        print(sumFunc(10,20))
    }
    
    
    
    //MARK: - 函数
    
    /*外部参数
        - 不会影响到函数内部的代码执行
        - 方便调用函数的程序员，更清晰各个参数的语义
    */
    func sumWtihExternalParameter(num1 x:Int,num2 y:Int) -> Int
    {
        return x + y
    }
    
    //函数格式 func 函数名(形参列表) -> 返回值类型
    func sum(x:Int,y:Int) -> Int
    {
        return x + y
    }
    
    //MARK: - 字典的演练
    func dictionaryPractice()
    {
        var dictOne = ["name":"Silent丶0_0","age":22] as [String : Any]
        let dictTwo = ["title":"IOS开发者","name":"猫爷爷的酱醋茶"]
        
        //将dictTwo的内容合并到dictOne
        for (k,v) in dictTwo
        {
            dictOne[k] = v
        }
        
        print(dictOne)
    }
    
    //MARK: - 数组容量
    func arrayCapacity()
    {
        //声明一个存放字符串的数组，并没有实现，也就是没有初始化，没有分配空间
        var arrayOne:[String]
        
        //初始化， Swift中用（）表示alloct init   //声明并实例化：var array:[String]()
        arrayOne = [String]()
        
        for i in 0..<16
        {
            //字符串的拼接
            arrayOne.append("猫爷爷的酱醋茶 \(i)")
            //如果插入元素时，当容量不够的时候，会在当前容量基础上*2，方便后续元素的添加
            print(arrayOne[i] + " - 容量 \(arrayOne.capacity)")
        }
        
    }
    
    //数组的合并
    func arrayMerge()
    {
        var arrayOne = ["猫爷爷的酱醋茶","应届"]
        let arrayTwo = ["毕业生","IOS开发者"]
        //数据类型相同的数组，可以直接相加合并
        arrayOne += arrayTwo
        print(arrayOne)
    }
    
    //数组
    func arrayProperty()
    {
        var array = ["猫爷爷的酱醋茶",CGPoint(x: 10, y: 10)] as [Any]
        array.append(120)
        array.remove(at: 1)
        array[0] = "Silent丶0_0"
        
        //删除所有元素，并且保留容量
        array.removeAll(keepingCapacity: true)
        print(array)
        print("容量为 \(array.capacity)")
        
        
    }
    
    //MARK: - 字符串的占位符
    func stringPlaceHolder()
    {
        let h = 8,m = 5,s = 6;
        let dateString = String(format: "%02d:%02d:%02d", arguments: [h,m,s]);
        print(dateString);
    }
    
    //MARK: - if let 实例
    func ifLet()
    {
        let name : String? = "猫爷爷的酱醋茶"
        let age : Int? = 20
        
        if let name = name,let age = age
        {
            print(name + String(age))
        }
    }


}

